package org.fastsyncer.manager.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.fastsyncer.common.constant.ConnectorConstant;
import org.fastsyncer.common.constant.MappingConstant;
import org.fastsyncer.common.entity.DatabaseConfig;
import org.fastsyncer.common.entity.LdapConfig;
import org.fastsyncer.common.entity.Mapping;
import org.fastsyncer.common.entity.MappingTask;
import org.fastsyncer.common.entity.WebServiceConfig;
import org.fastsyncer.connector.database.DatabaseConnector;
import org.fastsyncer.connector.framework.ConnectorFactory;
import org.fastsyncer.connector.ldap.LdapConnector;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ManagerUtil {

	private static Logger logger = LoggerFactory.getLogger(ManagerUtil.class);

	public static MappingTask parseMapping(String mapping)
			throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		MappingTask mappingTask = new MappingTask();

		// 1.解析连接器配置
		JSONObject rel = new JSONObject(mapping);

		// 2.获取mapping基本信息
		// 映射关系配置是否启用
		mappingTask.setEnable(rel.getBoolean("enable"));

		// 3.驱动模式
		String model = rel.getString("model");
		mappingTask.setModel(StringUtils.isNotBlank(model) ? model : mappingTask.getModel());
		// 映射关系配置名称
		mappingTask.setName(rel.getString("name"));

		// 4.解析过滤条件
		parseQuery(rel, mappingTask);

		// 5.解析驱动信息
		parseDriver(rel, mappingTask);

		// 6.解析转换策略
		parseConvertor(rel, mappingTask);

		// 7.解析高级配置
		parsePolicies(rel, mappingTask);
		return mappingTask;
	}

	/**
	 * @Title: parseConvertor
	 * @Description: 解析转换器配置
	 * @param rel
	 * @param mappingTask
	 * @throws JSONException
	 * @return: void
	 */
	private static void parseConvertor(JSONObject rel, MappingTask mappingTask) throws JSONException {
		// 解析转换器配置
		List<Mapping> mapList = mappingTask.getMappings();

		// 获取JSON转换配置
		JSONArray mappings = rel.getJSONArray("mappings");
		int tlen = mappings.length();
		for (int i = 0; i < tlen; i++) {
			// 如果没有配置转换就跳过
			if (mappings.getJSONObject(i).isNull("convertor")) {
				continue;
			}

			JSONObject convertorJson = mappings.getJSONObject(i).getJSONObject("convertor");
			// 获取当前目标源转换属性名称,ID、USERNAME
			JSONArray names = convertorJson.names();
			if (names == null) {
				break;
			}
			int nLen = names.length();
			if (nLen < 1) {
				break;
			}

			// 定义转换器
			Map<String, Map<String, String[]>> convertor = new HashMap<String, Map<String, String[]>>();
			for (int j = 0; j < nLen; j++) {
				// 属性名,ID
				String attrName = names.getString(j);
				// 获取当前属性的转换方式,{"prepend":["这是前缀"],"append":["这是后缀"]}
				JSONObject types = convertorJson.getJSONObject(attrName);
				// 解析属性转换类型
				Map<String, String[]> oprs = parseConvertOprs(types);
				// 放进转换器
				convertor.put(attrName, oprs);
			}

			// 保存目标源的转换配置
			mapList.get(i).setConvertor(convertor);
		}

	}

	// 解析属性的转换类型
	private static Map<String, String[]> parseConvertOprs(JSONObject types) throws JSONException {
		// 转换类型prepend、replace、append
		JSONArray names = types.names();
		if (names == null) {
			throw new JSONException("转换方式为空!");
		}
		int nLen = names.length();
		if (nLen < 1) {
			throw new JSONException("转换方式为空!");
		}

		// 定义属性的转换方式
		Map<String, String[]> oprs = new LinkedHashMap<String, String[]>();
		for (int i = 0; i < nLen; i++) {
			// 转换类型,append
			String oprType = names.getString(i);
			// 获取转换方式
			JSONArray oprArr = types.getJSONArray(oprType);
			int oLen = oprArr.length();
			String[] arr = new String[oLen];
			for (int j = 0; j < oLen; j++) {
				arr[j] = oprArr.getString(j);
			}
			oprs.put(oprType, arr);
		}
		return oprs;
	}

	public static void parseDriver(JSONObject rel, MappingTask mappingTask)
			throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 1.解析数据源
		parseDriverSource(rel, mappingTask);

		// 2.解析目标源
		parseDriverTarget(rel, mappingTask);
	}

	/**
	 * 解析数据库配置
	 * 
	 * @Title: getDataSource
	 * @Description: 解析数据库配置
	 * @param datasource
	 * @return: DataSourceConfig
	 * @throws JSONException
	 */
	private static DatabaseConfig getDataSource(JSONObject datasource) throws JSONException {
		String driverClassName = (String) datasource.get("driverClassName");
		String url = (String) datasource.get("url");
		String username = (String) datasource.get("username");
		String password = (String) datasource.get("password");
		String table = (String) datasource.get("table");
		String primaryKey = (String) datasource.get("primaryKey");
		DatabaseConfig dataSourceConfig = new DatabaseConfig(driverClassName, url, table, primaryKey, username,
				password);

		// 解析表列名属性
		JSONArray column = datasource.getJSONArray("column");
		int colLen = column.length();
		if (column != null && colLen > 0) {
			List<HashMap<String, Object>> cols = new ArrayList<>();
			for (int i = 0; i < colLen; i++) {
				HashMap<String, Object> col = new HashMap<>();
				JSONObject c = (JSONObject) column.get(i);
				col.put("name", c.get("name"));
				col.put("typeName", c.get("typeName"));
				col.put("type", c.get("type"));
				cols.add(col);
			}
			dataSourceConfig.setColumn(cols);
		}
		return dataSourceConfig;
	}

	/**
	 * 解析WebService配置
	 * 
	 * @Title: getWebService
	 * @Description: 解析WebService配置
	 * @param xml
	 * @return: XMLConfig
	 * @throws JSONException
	 */
	private static WebServiceConfig getWebService(JSONObject xml) throws JSONException {
		String url = (String) xml.get("url");
		String method = (String) xml.get("method");
		String primaryKey = (String) xml.get("primaryKey");
		WebServiceConfig xmlConfig = new WebServiceConfig(url, method, primaryKey);

		// 解析表列名属性
		JSONArray column = xml.getJSONArray("column");
		int colLen = column.length();
		if (column != null && colLen > 0) {
			List<HashMap<String, Object>> cols = new ArrayList<>();
			for (int i = 0; i < colLen; i++) {
				HashMap<String, Object> col = new HashMap<>();
				JSONObject c = (JSONObject) column.get(i);
				col.put("name", c.get("name"));
				col.put("typeName", c.get("typeName"));
				col.put("type", c.get("type"));
				cols.add(col);
			}
			xmlConfig.setColumn(cols);
		}
		return xmlConfig;
	}

	/**
	 * 解析getLdap配置
	 * 
	 * @Title: getLdap
	 * @Description: 解析getLdap配置
	 * @param ldap
	 * @return: ldapConfig
	 * @throws JSONException
	 */
	private static LdapConfig getLdap(JSONObject ldap) throws JSONException {
		String url = (String) ldap.get("url");
		String base = (String) ldap.get("base");
		String userDn = (String) ldap.get("userDn");
		String password = (String) ldap.get("password");
		LdapConfig ldapConfig = new LdapConfig(url, base, userDn, password);

		// 解析表列名属性
		JSONArray column = ldap.getJSONArray("column");
		int colLen = column.length();
		if (column != null && colLen > 0) {
			List<HashMap<String, Object>> cols = new ArrayList<>();
			for (int i = 0; i < colLen; i++) {
				HashMap<String, Object> col = new HashMap<>();
				JSONObject c = (JSONObject) column.get(i);
				col.put("name", c.get("name"));
				col.put("typeName", c.get("typeName"));
				col.put("type", c.get("type"));
				cols.add(col);
			}
			ldapConfig.setColumn(cols);
		}
		return ldapConfig;
	}

	/**
	 * 解析同步属性
	 * 
	 * @Title: parseFields
	 * @Description: 解析同步属性
	 * @param filedsArray
	 * @return: List<HashMap<String,Object>>
	 * @throws JSONException
	 */
	public static List<HashMap<String, Object>> parseFields(JSONArray filedsArray) throws JSONException {
		List<HashMap<String, Object>> sourceFileds = new ArrayList<HashMap<String, Object>>();
		int sLen = filedsArray.length();
		for (int i = 0; i < sLen; i++) {
			JSONObject object = filedsArray.getJSONObject(i);
			HashMap<String, Object> sfiled = new HashMap<String, Object>();
			sfiled.put("name", object.get("name"));
			sfiled.put("type", object.get("type"));
			sfiled.put("typeName", object.get("typeName"));
			sourceFileds.add(sfiled);
		}
		return sourceFileds;
	}

	// 解析所有的过滤条件
	public static void parseQuery(JSONObject rel, MappingTask mappingTask) throws JSONException {
		HashMap<String, List<HashMap<String, String>>> querys = new HashMap<String, List<HashMap<String, String>>>();
		JSONObject query = rel.getJSONObject("query");

		// 解析And过滤条件
		List<HashMap<String, String>> addList = parseQuery(query.getJSONArray(ConnectorConstant.OPERTION_QUERY_AND));
		querys.put(ConnectorConstant.OPERTION_QUERY_AND, addList);

		// 解析Or过滤条件
		List<HashMap<String, String>> orList = parseQuery(query.getJSONArray(ConnectorConstant.OPERTION_QUERY_OR));
		querys.put(ConnectorConstant.OPERTION_QUERY_OR, orList);

		// 保存解析条件
		mappingTask.setQuery(querys);
	}

	// 解析过滤条件
	public static List<HashMap<String, String>> parseQuery(JSONArray querys) throws JSONException {
		// 解析过滤条件
		List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
		int qLen = querys.length();
		for (int i = 0; i < qLen; i++) {
			HashMap<String, String> q = new HashMap<String, String>();
			JSONObject f = (JSONObject) querys.get(i);
			q.put("name", f.getString("name"));
			q.put("operator", f.getString("operator"));
			q.put("value", f.getString("value"));
			list.add(q);
		}
		return list;
	}

	// 解析策略
	public static void parsePolicies(JSONObject rel, MappingTask mappingTask) throws JSONException {
		HashMap<String, HashMap<String, String>> policyMap = new HashMap<>();
		JSONObject policy = (JSONObject) rel.get("policy");

		// 解析增量同步，暂时针对canel,logmnr实现
		if (!policy.isNull(MappingConstant.POLICY_INCREMENT)) {
			JSONObject policyInc = (JSONObject) policy.get(MappingConstant.POLICY_INCREMENT);
			HashMap<String, String> incMap = new HashMap<>();
			incMap.put("enable", policyInc.getString("enable"));

			// 增量类型
			String type = policyInc.getString("type");
			incMap.put("type", type);
			if (MappingConstant.POLICY_INCREMENT_MYSQL.equalsIgnoreCase(type)) {
				// 如果是mysql增量配置
				incMap.put("ip", policyInc.getString("ip"));
				incMap.put("port", String.valueOf(policyInc.getInt("port")));
				incMap.put("instance", policyInc.getString("instance"));
				incMap.put("database", policyInc.getString("database"));
				incMap.put("table", policyInc.getString("table"));
				policyMap.put(MappingConstant.POLICY_INCREMENT, incMap);

			} else if (MappingConstant.POLICY_INCREMENT_ORACLE.equalsIgnoreCase(type)) {
				// 如果是oracle增量配置
				incMap.put("scanAll", policyInc.getString("scanAll"));
				incMap.put("dictionary", String.valueOf(policyInc.getString("dictionary")));
				policyMap.put(MappingConstant.POLICY_INCREMENT, incMap);
			}
		}
		mappingTask.setPolicy(policyMap);
	}

	/**
	 * 解析数据源
	 * 
	 * @Title: parseDriverSource
	 * @Description: 解析数据源
	 * @param rel
	 * @param mappingTask
	 * @throws JSONException
	 * @return: void
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static void parseDriverSource(JSONObject rel, MappingTask mappingTask)
			throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 解析数据源
		JSONObject sourceMappingObj = rel.getJSONObject("sourceMapping");

		// org.fastsyncer.connector.database.DatabaseConnector
		switch (sourceMappingObj.getString("connector")) {
		case ConnectorConstant.DB_CONNECTOR:
			// 1.获取数据源配置
			DatabaseConfig sourceConfig = getDataSource(sourceMappingObj.getJSONObject("config"));

			// 2.获取数据量
			DatabaseConnector dbConnector = (DatabaseConnector) ConnectorFactory
					.getInstance(ConnectorConstant.DB_CONNECTOR);
			// 获取过滤条件
			String queryDatabaseFilter = dbConnector.getFilter(mappingTask.getQuery());
			Integer sourceRowCount = dbConnector.getBaseInfoConnector(sourceConfig, queryDatabaseFilter).getRowCount();

			// 3.获取同步数据源字段、类型
			List<HashMap<String, Object>> sourceDBFileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

			// 4.设置数据源配置、驱动类型、同步属性、数据量
			Mapping sourceMappingDatabase = new Mapping(sourceMappingObj.getString("name"), sourceConfig,
					ConnectorConstant.DB_CONNECTOR, sourceDBFileds, sourceRowCount);

			// 5.拼接数据源执行命令
			if (sourceDBFileds != null && sourceDBFileds.size() > 0) {
				String sourceExcuteCommond = dbConnector.getJdbcSQL(ConnectorConstant.OPERTION_QUERY, sourceConfig,
						sourceDBFileds, queryDatabaseFilter);
				sourceMappingDatabase.getExcuteCommond().put(ConnectorConstant.OPERTION_QUERY, sourceExcuteCommond);
				logger.info("sourceExcuteQueryCommond:" + sourceExcuteCommond);
			}
			mappingTask.setSourceMapping(sourceMappingDatabase);
			break;
		case ConnectorConstant.WS_CONNECTOR:
			// 1.获取数据源配置
			WebServiceConfig sourceWSConfig = getWebService(sourceMappingObj.getJSONObject("config"));

			// 2.获取同步数据源、目标源字段和数据源、目标源字段类型
			List<HashMap<String, Object>> sourceXmlFileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

			// 3.设置数据源配置、驱动类型、同步属性、数据量
			Mapping sourceMappingXml = new Mapping(sourceMappingObj.getString("name"), sourceWSConfig,
					ConnectorConstant.WS_CONNECTOR, sourceXmlFileds, 0);
			mappingTask.setSourceMapping(sourceMappingXml);
			break;
		case ConnectorConstant.LDAP_CONNECTOR:
			// 1.获取数据源配置
			LdapConfig sourceLdapConfig = getLdap(sourceMappingObj.getJSONObject("config"));

			// 2.获取同步数据源、目标源字段和数据源、目标源字段类型
			List<HashMap<String, Object>> sourceLdapFileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

			// 3.设置数据源配置、驱动类型、同步属性、数据量
			Mapping sourceMappingLdap = new Mapping(sourceMappingObj.getString("name"), sourceLdapConfig,
					ConnectorConstant.LDAP_CONNECTOR, sourceLdapFileds, 12);

			// 4.拼接数据源执行命令
			if (sourceLdapFileds != null && sourceLdapFileds.size() > 0) {
				// 获取过滤条件
				LdapConnector ldapConnector = (LdapConnector) ConnectorFactory.getInstance(ConnectorConstant.LDAP_CONNECTOR);
				String sourceExcuteCommond = ldapConnector.getFilter(mappingTask.getQuery());
				sourceMappingLdap.getExcuteCommond().put(ConnectorConstant.OPERTION_QUERY, sourceExcuteCommond);
				logger.info("sourceExcuteQueryCommond:" + sourceExcuteCommond);
			}
			mappingTask.setSourceMapping(sourceMappingLdap);
			break;
		default:
			break;
		}
	}

	/**
	 * 解析目标源
	 * 
	 * @Title: parseDriverTarget
	 * @Description: 解析目标源
	 * @param rel
	 * @param mappingTask
	 * @throws JSONException
	 * @return: void
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static void parseDriverTarget(JSONObject rel, MappingTask mappingTask)
			throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 解析目标源
		JSONArray targetMappingsObj = rel.getJSONArray("mappings");
		int tlen = targetMappingsObj.length();
		List<Mapping> targetMappingList = new ArrayList<>();
		for (int i = 0; i < tlen; i++) {
			JSONObject targetMappingObj = targetMappingsObj.getJSONObject(i);
			switch (targetMappingObj.getString("connector")) {
			case ConnectorConstant.DB_CONNECTOR:
				// 1.获取同步数据源、目标源字段和数据源、目标源字段类型
				List<HashMap<String, Object>> targetDBFileds = parseFields(targetMappingObj.getJSONArray("fileds"));

				// 2.获取数据源配置
				DatabaseConfig targetDBConfig = getDataSource(targetMappingObj.getJSONObject("config"));

				// 3.获取数据量
				DatabaseConnector dbConnector = (DatabaseConnector) ConnectorFactory
						.getInstance(ConnectorConstant.DB_CONNECTOR);
				Integer targetRowCount = dbConnector.getBaseInfoConnector(targetDBConfig).getRowCount();

				// 设置数据源配置、驱动类型、同步属性、数据量
				Mapping targetMapping = new Mapping(targetMappingObj.getString("name"), targetDBConfig,
						ConnectorConstant.DB_CONNECTOR, targetDBFileds, targetRowCount);

				// 4.拼接数据源执行命令
				String targetExcuteCommond = null;
				if (targetDBFileds != null && targetDBFileds.size() > 0) {
					// 4.1解析插入语句
					targetExcuteCommond = dbConnector.getJdbcSQL(ConnectorConstant.OPERTION_INSERT, targetDBConfig,
							targetDBFileds, null);
					targetMapping.getExcuteCommond().put(ConnectorConstant.OPERTION_INSERT, targetExcuteCommond);
					logger.info("targetExcuteInsertCommond:" + targetExcuteCommond);

					// 4.2解析修改语句
					targetExcuteCommond = dbConnector.getJdbcSQL(ConnectorConstant.OPERTION_UPDATE, targetDBConfig,
							targetDBFileds, null);
					targetMapping.getExcuteCommond().put(ConnectorConstant.OPERTION_UPDATE, targetExcuteCommond);
					logger.info("targetExcuteUpdateCommond:" + targetExcuteCommond);

					// 4.3解析删除语句
					targetExcuteCommond = dbConnector.getJdbcSQL(ConnectorConstant.OPERTION_DELETE, targetDBConfig,
							targetDBFileds, null);
					targetMapping.getExcuteCommond().put(ConnectorConstant.OPERTION_DELETE, targetExcuteCommond);
					logger.info("targetExcuteDeleteCommond:" + targetExcuteCommond);
				}
				targetMappingList.add(targetMapping);
				break;
			case ConnectorConstant.WS_CONNECTOR:
				// 1.获取数据源配置
				WebServiceConfig targetWSConfig = getWebService(targetMappingObj.getJSONObject("config"));

				// 2.获取同步数据源、目标源字段和数据源、目标源字段类型
				List<HashMap<String, Object>> targetXmlFileds = parseFields(targetMappingObj.getJSONArray("fileds"));

				// 3.设置数据源配置、驱动类型、同步属性、数据量
				Mapping targetMappingXml = new Mapping(targetMappingObj.getString("name"), targetWSConfig,
						ConnectorConstant.WS_CONNECTOR, targetXmlFileds, 0);
				targetMappingList.add(targetMappingXml);
				break;
			case ConnectorConstant.LDAP_CONNECTOR:
				// 1.获取数据源配置
				LdapConfig targetLdapConfig = getLdap(targetMappingObj.getJSONObject("config"));

				// 2.获取同步数据源、目标源字段和数据源、目标源字段类型
				List<HashMap<String, Object>> targetLdapFileds = parseFields(targetMappingObj.getJSONArray("fileds"));

				// 3.设置数据源配置、驱动类型、同步属性、数据量
				Mapping targetMappingLdap = new Mapping(targetMappingObj.getString("name"), targetLdapConfig,
						ConnectorConstant.LDAP_CONNECTOR, targetLdapFileds, 0);
				targetMappingList.add(targetMappingLdap);
				break;
			default:
				break;
			}
		}
		mappingTask.setMappings(targetMappingList);
	}

}
